package utils

import (
	"time"
)

/*
**

	阻塞计数器
	TryBegin 计数失败返回false
	Begin, Begin2: 阻塞

	* 可以用再允许几个纤程进入的时候
*/
type BlockCounter struct {
	Id       string
	blockMap GSyncMapGroup
	cnt_chan chan int
	max      int32
	// cnt      int32   直接读取cnt_chan的大小, 准确
}

type BlockItem struct {
	id        uint64
	callstack string // Block时的callstack
	beginT    time.Time
	owner     *BlockCounter
	DebugInfo string
}

func NewBlockCounter(max int32) *BlockCounter {
	rval := &BlockCounter{
		cnt_chan: make(chan int, max),
		max:      max,
	}

	for i := 0; i < int(max); i++ {
		rval.cnt_chan <- 1
	}
	return rval
}

func (this *BlockCounter) ConfigID(id string) *BlockCounter {
	this.Id = id
	return this
}

func (this *BlockCounter) UseCnt() int {
	return int(this.max) - len(this.cnt_chan)
}

func (this *BlockItem) EndUse() {
	this.owner.EndUse()
	this.owner.blockMap.Remove(this.id)
}

/**
 * 显示成功的连接.
 */
func (this *BlockCounter) String() string {
	var bb BytesBuilder
	bb.Appendf("max:%d, block cnt:%d\r\n", this.max, this.UseCnt())
	lst := this.blockMap.ListSorted(func(iItm, jItm interface{}) bool {
		return iItm.(*BlockItem).beginT.Before(jItm.(*BlockItem).beginT)
	})
	for _, value := range lst {
		itm := value.(*BlockItem)
		bb.Appendf("id:%d, start block time:%s, debuginfo:%s, callstack:\r\n%s\r\n\r\n",
			itm.id, DateTimeString2(itm.beginT), itm.DebugInfo, itm.callstack)
	}
	return bb.String()
}

/*
*

	用BlockItem 进行还回, 不要直接EndUse
	callstack是调用时产生的
*/
func (this *BlockCounter) BeginUse2() *BlockItem {
	this.BeginUse()
	itm := &BlockItem{
		id:        NewObjectDNA64(),
		callstack: GetCallStack(0),
		owner:     this,
		beginT:    time.Now(),
	}
	this.blockMap.Set(itm.id, itm)
	return itm
}

func (this *BlockCounter) TryBeginUse() bool {
	select {
	case <-this.cnt_chan:
		//atomic.AddInt32(&this.cnt, 1)
		return true
	default:
		return false
	}
	return false
}

func (this *BlockCounter) BeginUse() {
	<-this.cnt_chan
	//atomic.AddInt32(&this.cnt, 1)
}

func (this *BlockCounter) EndUse() {
	this.cnt_chan <- 1
	//atomic.AddInt32(&this.cnt, -1)    // 不是原子操作, 可能在 <- 又被借出了, 造成UseCnt不准确的情况
}
